package com.punch.order.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.punch.common.dto.order.OrderDTO;
import com.punch.common.dto.order.OrderDetailDTO;
import com.punch.common.enums.order.OrderStatusEnum;
import com.punch.common.exception.BusinessException;
import com.punch.common.exception.CommonExceptionEnum;
import com.punch.common.feign.FeignStudentService;
import com.punch.common.vo.course.classes.AppGoodsVO;
import com.punch.common.vo.course.classes.FeignClassVO;
import com.punch.common.vo.user.student.StudentVO;
import com.punch.order.exception.OrderExceptionEnum;
import com.punch.order.mapper.OrderCourseSnapshotMapper;
import com.punch.order.model.OrderCourseSnapshot;
import com.punch.order.model.OrderGoodsSnapshot;
import com.punch.order.service.OrderCourseSnapshotService;
import com.punch.order.service.OrderGoodsSnapshotService;
import com.punch.order.util.ArrayUtil;
import com.punch.order.util.RedissonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Period;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

/**
 * <p>
 * 订单课程快照表 服务实现类
 * </p>
 *
 * @author wjj
 * @since 2020-12-09
 */
@Service
@Slf4j
public class OrderCourseSnapshotServiceImpl extends ServiceImpl<OrderCourseSnapshotMapper, OrderCourseSnapshot> implements OrderCourseSnapshotService {


    @Resource
    private FeignStudentService feignStudentService;

    @Resource
    private OrderGoodsSnapshotService orderGoodsSnapshotService;

    /**
     * 创建三点半课程订单
     *
     * @param orderDTO 订单信息
     * @param orderId  订单id
     * @param orderSN  订单编号
     */
    @Override
    public void createOrderCourse(OrderDTO orderDTO, Long userId, Long orderId, String orderSN) {
        long startTime = System.currentTimeMillis();
        //获取课程订单
        OrderDetailDTO orderDetailDTO = orderDTO.getProductList()
                .stream()
                .filter(product -> 0 == product.getProductType()).findFirst()
                .orElseThrow(() -> new BusinessException(CommonExceptionEnum.PARAMETER_LESS));
        orderDTO.setBody("课程:" + orderDetailDTO.getProductName());
        StudentVO studentVO = feignStudentService.getFeignStudentById(userId, orderDTO.getStudentId());
        log.info("请求学生信息[{}]", System.currentTimeMillis() - startTime);
        if (studentVO == null || studentVO.getStudentId() == null) {
            throw new BusinessException(OrderExceptionEnum.FEIGN_ERROR.getCode(), "学生不存在或您暂未添加该学生");
        }
        FeignClassVO classVO = RedissonUtil.redissonLock(orderDetailDTO.getProductId());
        log.info("请求课程信息[{}]", System.currentTimeMillis() - startTime);
        //订单验证
        //同一个学生在同一个班级只能报一次
        sameClassSignUpValidate(orderDetailDTO.getProductId(), orderDTO.getStudentId(), orderDTO.getStudentName());
        //同一天（同一时间段）只能报一门课程
        sameTimeOnlyOneClassValidate(orderDTO.getStudentId(), orderDTO.getStudentName(), classVO.getCourseWeek(), classVO.getClassTime());
        //报名费用不超过150的限制，平均每月，每个学期按4个月算
        signUPFeeValidate(orderDTO.getStudentId(), orderDTO.getStudentName());
        //报名只能报对应适用年级的课程
        courseGradeValidate(String.valueOf(studentVO.getGradeYear()), classVO.getCourseGrade(), classVO.getCourseGradeName());
        OrderDetailDTO material = moneyValidate(classVO, orderDetailDTO, orderDTO);//返回是否包含教具订单
        //创建订单
        OrderCourseSnapshot orderCourseSnapshot = new OrderCourseSnapshot();
        orderCourseSnapshot.setOrderId(orderId);
        orderCourseSnapshot.setOrderSN(orderSN);
        //包含教具
        Optional.ofNullable(material).ifPresent((m) -> {
            AppGoodsVO material1 = classVO.getMaterial();
            OrderGoodsSnapshot orderGoodsSnapshot = new OrderGoodsSnapshot();
            orderGoodsSnapshot.setOrderId(orderId);
            orderGoodsSnapshot.setOrderSN(orderSN);
            orderGoodsSnapshot.setCourseId(classVO.getCourseId());
            orderGoodsSnapshot.setCourseClassId(classVO.getCourseClassId());
            orderGoodsSnapshot.setGoodsId(m.getProductId());
            orderGoodsSnapshot.setGoodsAmount(m.getTotalAmount());
            orderGoodsSnapshot.setGoodsName(m.getProductName());
            orderGoodsSnapshot.setGoodsCover(material1.getGoodsCover());
            orderGoodsSnapshot.setGoodsTitle(material1.getGoodsTitle());
            //供应商信息
//            orderGoodsSnapshot.setSupplierId(classVO.getSupplierId());
//            orderGoodsSnapshot.setSupplierName(classVO.getSupplierName());
            orderGoodsSnapshotService.save(orderGoodsSnapshot);
            orderCourseSnapshot.setGoodsFlag(1);
            orderDTO.setBody(orderDTO.getBody() + ";教材/教具:" + m.getProductName());
        });
        saveOrderCourse(orderCourseSnapshot, studentVO, classVO);
        log.info("保存花费快照[{}]", System.currentTimeMillis() - startTime);
    }

    /**
     * 是否报名
     *
     * @param classId   课程班级id
     * @param studentId 学生id
     * @return boolean
     */
    @Override
    public boolean isSignUp(Long classId, Long studentId) {
        QueryWrapper<OrderCourseSnapshot> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(OrderCourseSnapshot::getCourseClassId, classId)
                .eq(OrderCourseSnapshot::getStudentId, studentId)
                .eq(OrderCourseSnapshot::getStatus, OrderStatusEnum.HAVE_PAY.getCode());
        OrderCourseSnapshot orderCourseSnapshot = getOne(queryWrapper);
        if (orderCourseSnapshot == null) {
            return false;
        }
        return true;
    }


    /**
     * 同一个学生在同一个班级只能报一次
     *
     * @param courseClassId 班级
     * @param studentId     学生id
     * @param studentName   学生姓名
     */
    private void sameClassSignUpValidate(Long courseClassId, Long studentId, String studentName) {
        QueryWrapper<OrderCourseSnapshot> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(OrderCourseSnapshot::getCourseClassId, courseClassId)
                .eq(OrderCourseSnapshot::getStudentId, studentId);
        OrderCourseSnapshot orderCourseSnapshotEX = getOne(queryWrapper);
        queryWrapper = null;//help gc
        if (orderCourseSnapshotEX == null) {
            return;
        }
        Integer status = orderCourseSnapshotEX.getStatus();
        //有待支付的订单
        StringBuilder sb = new StringBuilder();
        sb.append("该课程 ").append(studentName);
        if (status == OrderStatusEnum.TO_PAY.getCode()) {
            sb.append(" 有待支付的订单,请到我的订单列表进行支付!");
            throw new BusinessException(OrderExceptionEnum.STUDENT_ORDER_ERROR.getCode(), sb.toString());
        }
        if (status == OrderStatusEnum.HAVE_PAY.getCode()) {
            sb.append(" 已报名,无需重复报名!");
            throw new BusinessException(OrderExceptionEnum.STUDENT_ORDER_ERROR.getCode(), sb.toString());
        }

    }

    /**
     * 同一天（同一时间段）只能报一门课程
     *
     * @param studentId   学生id
     * @param studentName 学生姓名
     */
    private void sameTimeOnlyOneClassValidate(Long studentId, String studentName, String courseWeek, String classTime) {
        //同一个学生同一时间只能报名一个课程
        QueryWrapper<OrderCourseSnapshot> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(OrderCourseSnapshot::getStudentId, studentId);
        List<OrderCourseSnapshot> orderCourseSnapshotList = list(queryWrapper);
        queryWrapper = null;//help gc
        if (CollUtil.isNotEmpty(orderCourseSnapshotList)) {
            String[] currentClassWeekArr = courseWeek.split(",");
            String[] currentClassTimeArr = classTime.split("~");
            //当前报名的课程时间段
            LocalTime currentClassTimeStart = LocalTime.parse(currentClassTimeArr[0]);
            LocalTime currentClassTimeEnd = LocalTime.parse(currentClassTimeArr[1]);
            orderCourseSnapshotList.forEach(courseOrder -> {
                String[] weekArr = courseOrder.getCourseWeek().split(",");
                Set<String> sameDaySet = ArrayUtil.getSame(currentClassWeekArr, weekArr);
                //如果有相同的，则进行时间段比较,假设已经报名课程的时间段是 3:00~4:00,报名课程的时间点开始时间或者结束时间在这范围内则返回错误信息
                if (CollUtil.isNotEmpty(sameDaySet)) {
                    String[] timeArr = courseOrder.getClassTime().split("~");
                    //已经报名课程的时间段
                    LocalTime classTimeStart = LocalTime.parse(timeArr[0]);
                    LocalTime classTimeEnd = LocalTime.parse(timeArr[1]);
                    //时间段相同
                    if (currentClassTimeStart.equals(classTimeStart) || currentClassTimeEnd.equals(classTimeEnd)) {
                        StringBuilder sb = new StringBuilder();
                        sb.append(studentName).append("已报名 ")
                                .append(courseOrder.getCourseName())
                                .append(" 报名失败");
                        throw new BusinessException(OrderExceptionEnum.STUDENT_ORDER_ERROR.getCode(), sb.toString());
                    }
                    //已参加课程的时间段与当前课程比较
                    if ((classTimeStart.isBefore(currentClassTimeStart) && classTimeEnd.isAfter(currentClassTimeStart))
                            || (classTimeStart.isBefore(currentClassTimeEnd) && classTimeEnd.isAfter(currentClassTimeEnd))) {
                        StringBuilder sb = new StringBuilder();
                        sb.append(studentName).append("已报名 ")
                                .append(courseOrder.getCourseName())
                                .append(" 报名失败");
                        throw new BusinessException(OrderExceptionEnum.STUDENT_ORDER_ERROR.getCode(), sb.toString());
                    }
                    //当前课程的时间段与已参加课程比较
                    if ((currentClassTimeStart.isBefore(classTimeStart) && currentClassTimeEnd.isAfter(classTimeStart))
                            || (currentClassTimeStart.isBefore(classTimeEnd) && currentClassTimeEnd.isAfter(classTimeEnd))) {
                        StringBuilder sb = new StringBuilder();
                        sb.append(studentName).append("已报名 ")
                                .append(courseOrder.getCourseName())
                                .append(" 报名失败");
                        throw new BusinessException(OrderExceptionEnum.STUDENT_ORDER_ERROR.getCode(), sb.toString());
                    }
                }
            });
        }
    }

    /**
     * 费用不超过150的限制，平均每月，每个学期按4个月算
     *
     * @param studentId   学生
     * @param studentName 学生姓名
     */
    private void signUPFeeValidate(Long studentId, String studentName) {
        //江西上饶教育局对每个学生每个月金额不能超过150限制 9月1日 正式上课; 2021年1月29日 开始放寒假。本学期共计21.5周。
        QueryWrapper<OrderCourseSnapshot> queryWrapper = new QueryWrapper<>();
        LocalDate semesterStart = LocalDate.of(2020, 9, 1);
        LocalDate semesterEnd = LocalDate.of(2021, 1, 29);
        queryWrapper.lambda().eq(OrderCourseSnapshot::getStudentId, studentId)
                .between(OrderCourseSnapshot::getCreateTime, semesterStart, semesterEnd);
        List<OrderCourseSnapshot> list = list(queryWrapper);
        queryWrapper = null;//help gc
        Integer totalMoneyFee = 0;
        for (OrderCourseSnapshot orderCourseSnapshot : list) {
            totalMoneyFee += orderCourseSnapshot.getCourseTotalAmount();
        }
        if (totalMoneyFee != 0) {
            int months = Period.between(semesterStart, semesterEnd).getMonths() + 1;
            double avgAmount = totalMoneyFee / 100 / months;
            if (avgAmount > 150) {
                throw new BusinessException(OrderExceptionEnum.STUDENT_ORDER_ERROR.getCode(), studentName + "报名课程金额已达到教育局收费标准");
            }
        }
    }

    /**
     * 报名只能报对应适用年级的课程
     *
     * @param studentGrade    学生年级
     * @param courseGrade     课程适用年级
     * @param courseGradeName 课程适用年级中文
     */
    private void courseGradeValidate(String studentGrade, String courseGrade, String courseGradeName) {
        boolean flag = false;
        String[] courseGradeArr = courseGrade.split(",");
        for (String gradeStr : courseGradeArr) {
            if (studentGrade.equals(gradeStr)) {
                flag = true;
            }
        }
        if (!flag) {
            throw new BusinessException(OrderExceptionEnum.STUDENT_ORDER_ERROR.getCode(), "该课程仅适用于" + courseGradeName);
        }
    }


    /**
     * 验证金额
     *
     * @param classVO        课程表单
     * @param orderDetailDTO 课程班级表单
     */
    private OrderDetailDTO moneyValidate(FeignClassVO classVO, OrderDetailDTO orderDetailDTO, OrderDTO orderDTO) {
        AtomicReference<OrderDetailDTO> materialAtomic = new AtomicReference<>();
        Long courseClassId = orderDetailDTO.getProductId();
        Integer commitOrderAmount = orderDTO.getOrderAmount();//提交订单总金额
        //提交课程的总价验证
        Integer calculateCourseClassAmount = orderDetailDTO.getPrice() * orderDetailDTO.getQuantity();//提交的课程单价*数量
        Integer commitTotalCourseClassAmount = orderDetailDTO.getTotalAmount();
        if (!calculateCourseClassAmount.equals(commitTotalCourseClassAmount)) {
            log.error("提交课程:[{}]金额计算有误,计算的总价:[{}],提交的总价:[{}]", courseClassId, calculateCourseClassAmount, commitTotalCourseClassAmount);
            throw new BusinessException(OrderExceptionEnum.COURSE_AMOUNT_ERROR);
        }
        //数据库课程班级价格
        Integer dbCourseClassAmount = classVO.getCourseHourPrice() * classVO.getCourseHourCount();
        orderDTO.getProductList()
                .stream()
                .filter(product -> 1 == product.getProductType()).findFirst().ifPresent((material) -> {
            Integer calculateMaterialAmount = material.getPrice() * material.getQuantity();//单价*数量
            Integer materialTotalAmount = material.getTotalAmount();//订单教具金额
            if (!calculateMaterialAmount.equals(materialTotalAmount)) {
                log.error("提交教具:[{}]金额计算有误,计算的总价:[{}],提交的总价:[{}]", courseClassId, calculateMaterialAmount, materialTotalAmount);
                throw new BusinessException(OrderExceptionEnum.COURSE_AMOUNT_ERROR);
            }
            Integer plusTotalAmount = materialTotalAmount + commitTotalCourseClassAmount;//订单课程与教具相加总额
            //表单中订单总额=表单教具金额+表单课程金额
            if (!plusTotalAmount.equals(commitOrderAmount)) {
                log.error("提交订单总金额:[{}]金额计算有误,计算的总价:[{}],提交的总价:[{}]", courseClassId, plusTotalAmount, commitOrderAmount);
                throw new BusinessException(OrderExceptionEnum.COURSE_AMOUNT_ERROR);
            }
            //班级课程教具金额
            Integer courseMaterialAmount = classVO.getMaterial().getGoodsPrice();
            Integer courseClassPlusTotalAmount = dbCourseClassAmount + courseMaterialAmount;
            //表单中订单总额=数据库班级课程总额
            if (!commitOrderAmount.equals(courseClassPlusTotalAmount)) {
                log.error("数据库课程班级:[{}]的总价:[{}]与提交的订单总金额:[{}]不相等", courseClassId, courseClassPlusTotalAmount, commitOrderAmount);
                throw new BusinessException(OrderExceptionEnum.COURSE_AMOUNT_ERROR);
            }
            materialAtomic.set(material);
        });
        //后面的无需判断了
        if (materialAtomic.get() != null) {
            return materialAtomic.get();
        }
        //教具必须购买
        if (materialAtomic.get() == null && classVO.getBuyFlag() == 1) {
            throw new BusinessException(OrderExceptionEnum.MATERIAL_MUST_BUY);
        }
        //没有教具价格
        //订单总金额!=课程金额
        if (!commitTotalCourseClassAmount.equals(commitOrderAmount)) {
            log.error("提交订单总金额与班级信息:[{}]金额比较有误,班级的总价:[{}],提交的总价:[{}]", courseClassId, commitTotalCourseClassAmount, commitOrderAmount);
            throw new BusinessException(OrderExceptionEnum.COURSE_AMOUNT_ERROR);
        }
        // 数据库班级课程总金额!=课程金额
        if (!dbCourseClassAmount.equals(commitOrderAmount)) {
            log.error("数据库课程班级:[{}]的总价:[{}]与提交的订单总金额:[{}]不相等", courseClassId, dbCourseClassAmount, commitOrderAmount);
            throw new BusinessException(OrderExceptionEnum.COURSE_AMOUNT_ERROR);
        }
        return materialAtomic.get();
    }

    /**
     * 保存课程快照
     *
     * @param orderCourseSnapshot 课程快照
     * @param studentVO           学生信息
     * @param classVO             课程信息
     */
    private void saveOrderCourse(OrderCourseSnapshot orderCourseSnapshot, StudentVO studentVO, FeignClassVO classVO) {
        //课程信息
        Long courseClassId = classVO.getCourseClassId();
        orderCourseSnapshot.setCourseId(courseClassId);
        orderCourseSnapshot.setCourseName(classVO.getCourseName());
        orderCourseSnapshot.setCourseClassId(courseClassId);
        orderCourseSnapshot.setCourseClassName(classVO.getCourseClassName());
        orderCourseSnapshot.setCourseCreateId(classVO.getCourseCreateId());
        orderCourseSnapshot.setCourseCreateType(classVO.getType());
        orderCourseSnapshot.setCourseCreateName(classVO.getCourseCreateName());
        Integer courseHourPrice = classVO.getCourseHourPrice();
        Integer courseHourCount = classVO.getCourseHourCount();
        orderCourseSnapshot.setCourseHourCount(courseHourCount);
        Integer courseTotalAmount = courseHourPrice * courseHourCount;
        orderCourseSnapshot.setCourseTotalAmount(courseTotalAmount);
        orderCourseSnapshot.setCourseTime(classVO.getCourseTime());
        orderCourseSnapshot.setClassTime(classVO.getClassTime());
        orderCourseSnapshot.setCourseWeek(classVO.getCourseWeek());
        //学生信息
        orderCourseSnapshot.setStudentId(studentVO.getStudentId());
        orderCourseSnapshot.setStudentName(studentVO.getName());
        orderCourseSnapshot.setStudentSex(studentVO.getSex());
        orderCourseSnapshot.setSchoolId(studentVO.getSchoolId());
        orderCourseSnapshot.setSchoolName(studentVO.getSchoolName());
        orderCourseSnapshot.setStudentGrade(studentVO.getGradeYear());
        orderCourseSnapshot.setStudentClass(studentVO.getGradeClass());
        orderCourseSnapshot.setGuardianMobile(studentVO.getGuardianMobile());
        //授课信息
        orderCourseSnapshot.setTeacherName(classVO.getTeacherName());
        orderCourseSnapshot.setClassRoomName(classVO.getClassRoomName());
        orderCourseSnapshot.setStatus(OrderStatusEnum.TO_PAY.getCode());
        save(orderCourseSnapshot);
    }

}
